#include <sstream>
#include "Database.h"
#include "DbValue.h"

//Database*	Database::_instance = NULL;

Database::Database()
{
}

//Database*	Database::GetDatabase()
//{
//	if (Database::_instance == NULL)
//		Database::_instance = new Database();
//	return Database::_instance;
//}

const unsigned int	Database::Open(const std::string& dbPath)
{
	if (sqlite3_open(dbPath.c_str(), &_db) != SQLITE_OK)
		return -1;
	return 0;
}

const unsigned int	Database::Close()
{
	sqlite3_close(_db);
	return 0;
}

const unsigned int	Database::Execute(const std::string& statement, DbResult* result)
{
	sqlite3_stmt*	stmt;
	int				step_rc;
	int				step_nbCol;

	if (sqlite3_prepare(_db, statement.c_str(), statement.length(), &stmt, 0) != SQLITE_OK)
		return -1;
	if ((step_nbCol = sqlite3_column_count(stmt)) > 0)
	{
		DbValue*	ptrnull = 0;
		while ((step_rc = sqlite3_step(stmt)) == SQLITE_ROW)
		{
			std::map<std::string, IDbValue*>*	row = new std::map<std::string, IDbValue*>();
			int	iCol = 0;
			while (iCol < step_nbCol)
			{
				if (sqlite3_column_type(stmt, iCol) == SQLITE_NULL)
					row->insert(std::make_pair(std::string(sqlite3_column_name(stmt, iCol)), ptrnull));
				if (sqlite3_column_type(stmt, iCol) == SQLITE_INTEGER)
					row->insert(std::make_pair(std::string(sqlite3_column_name(stmt, iCol)), new DbValue(sqlite3_column_int(stmt, iCol))));
				if (sqlite3_column_type(stmt, iCol) == SQLITE_FLOAT)
					row->insert(std::make_pair(std::string(sqlite3_column_name(stmt, iCol)), new DbValue(sqlite3_column_double(stmt, iCol))));
				if (sqlite3_column_type(stmt, iCol) == SQLITE_TEXT)
				{
					std::stringstream	text;
					text << sqlite3_column_text(stmt, iCol);
					row->insert(std::make_pair(std::string(sqlite3_column_name(stmt, iCol)), new DbValue(text.str())));
				}
				iCol++;
			}
			result->push_back(row);
		}
		if (step_rc != SQLITE_DONE)
		{
			//check value et diff return, todo gestion erreur
			return -1;
		}
	}
	if (sqlite3_finalize(stmt) != SQLITE_OK)
		return -1;
	return 0;
}

const unsigned int	Database::Execute(const std::string& statement)
{
	sqlite3_stmt*	stmt;
	int				step_rc;

	if (sqlite3_prepare(_db, statement.c_str(), statement.length(), &stmt, 0) != SQLITE_OK)
		return -1;
	if ((step_rc = sqlite3_step(stmt)) != SQLITE_DONE)
	{
		//check value et diff return, todo gestion erreur
		return -1;
	}
	if (sqlite3_finalize(stmt) != SQLITE_OK)
		return -1;
	return 0;
}

IDatabase::DbResult*			Database::SelectAllUser()
{
	//load depuis la base tout les users
	DbResult*	result = new DbResult();
	this->Execute("SELECT * FROM users;", result);
	return result;
}

IDatabase::DbResult*			Database::SelectUser(const unsigned int id)
{
	//load depuis la base le user 'id'
	DbResult*	result = new DbResult();
	std::stringstream	query;
	query << "SELECT * FROM users WHERE id=" << id << ";";
	this->Execute(query.str(), result);
	return result;
}

IDatabase::DbResult*			Database::SelectUser(const std::string& login)
{
	//load depuis la base le user 'login'
	DbResult*	result = new DbResult();
	std::stringstream	query;
	query << "SELECT * FROM users WHERE login=" << login << ";";
	this->Execute(query.str(), result);
	return result;
}

void				Database::UpdateUser(const unsigned int id, const std::string& login, const std::string& password)
{
	std::stringstream	query;
	query << "UPDATE users SET login=" << login << ", password=" << password << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const unsigned int	Database::CreateUser(const std::string& login, const std::string& password)
{
	std::stringstream	query;
	query << "INSERT INTO users(login, password) VALUES(" << login << "," << password << ");";
	this->Execute(query.str());
	/*DbResult*	result = this->SelectUser(login);
	unsigned int	id = (*result->at(0))["id"]->GetValueInt();
	delete result;*/
	return this->SelectUserId(login);
}

void				Database::DeleteAllUser()
{
	std::stringstream	query;
	query << "DELETE FROM users;";
	this->Execute(query.str());
}

void				Database::DeleteUser(const unsigned int id)
{
	std::stringstream	query;
	query << "DELETE FROM users WHERE id=" << id << ";";
	this->Execute(query.str());
}

//// User db funtions
const unsigned int	Database::SelectUserId(const std::string& login)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT id FROM users WHERE login=" << login << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["id"]->GetValueInt();
}

std::string			Database::SelectUserLogin(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT login FROM users WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["login"]->GetValueString();
}

void				Database::UpdateUserLogin(const unsigned int id, const std::string& login)
{
	std::stringstream	query;
	query << "UPDATE users SET login=" << login << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

std::string			Database::SelectUserPassword(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT password FROM users WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["password"]->GetValueString();
}

void				Database::UpdateUserPassword(const unsigned int id, const std::string& password)
{
	std::stringstream	query;
	query << "UPDATE main.users SET password=" << password << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

//// ChannelManager db functions
IDatabase::DbResult*			Database::SelectAllChannel()
{
	DbResult*	result = new DbResult();
	std::stringstream	query;
	query << "SELECT * FROM channels;";
	this->Execute(query.str(), result);
	return result;
}

IDatabase::DbResult*			Database::SelectChannel(const unsigned int idParent, const std::string& name)
{
	DbResult*	result = new DbResult();
	std::stringstream	query;
	query << "SELECT * FROM channels WHERE idParent=" << idParent << " AND name=" << name << ";";
	this->Execute(query.str(), result);
	return result;
}

//void				Database::UpdateAllChannel()
//{}
//
//void				Database::UpdateChannel(const unsigned int id)
//{}
//
const unsigned int	Database::CreateChannel(const unsigned int idParent, const std::string& name, const unsigned int maxNameLenght, const std::string& password, const unsigned int maxPasswordLenght, const std::string& motd, const unsigned int maxMotdLenght, const std::string& description, const unsigned int maxDescriptionLenght, const bool visible, const unsigned int maxUser, const unsigned int maxTalker)
{
	std::stringstream	query;
	query << "INSERT INTO channels(parentId, name, maxNameLenght, password, maxPasswordLenght, motd, maxMotdLenght, description, maxDescriptionLenght, visibility, maxUser, maxTalker) VALUES(-1," << name << "," << maxNameLenght << "," << password << "," << maxPasswordLenght << "," << motd << "," << maxMotdLenght << "," << description << "," << maxDescriptionLenght << "," << visible << "," << maxUser << "," << maxTalker << ");";
	this->Execute(query.str());
	return this->SelectChannelId(idParent, name);

}

void				Database::DeleteAllChannel()
{
	std::stringstream	query;
	query << "DELETE FROM channels";
	this->Execute(query.str());
}

void				Database::DeleteChannel(const unsigned int id)
{
	std::stringstream	query;
	query << "DELETE FROM channels WHERE id=" << id << ";";
	this->Execute(query.str());
}

//// Channel db functions
const unsigned int	Database::SelectChannelId(const unsigned int idParent, const std::string& name)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT id FROM channels WHERE idParent=" << idParent << " AND name=" << name << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["id"]->GetValueInt();
}

const unsigned int	Database::SelectChannelIdParent(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT idParent FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["idParent"]->GetValueInt();
}

void				Database::UpdateChannelIdParent(const unsigned int id, const unsigned int idParent)
{
	std::stringstream	query;
	query << "UPDATE channels SET idParent=" << idParent << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const std::string&	Database::SelectChannelName(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT name FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["name"]->GetValueString();
}

void				Database::UpdateChannelName(const unsigned int id, const std::string& name)
{
	std::stringstream	query;
	query << "UPDATE channels SET name=" << name << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const unsigned int	Database::SelectChannelMaxNameLenght(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT maxNameLenght FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["maxNameLenght"]->GetValueInt();
}

void				Database::UpdateChannelMaxNameLenght(const unsigned int id, const unsigned int maxNameLenght)
{
	std::stringstream	query;
	query << "UPDATE channels SET maxNameLenght=" << maxNameLenght << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const std::string&	Database::SelectChannelPassword(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT password FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["password"]->GetValueString();
}

void				Database::UpdateChannelPassword(const unsigned int id, const std::string& password)
{
	std::stringstream	query;
	query << "UPDATE channels SET password=" << password << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const unsigned int	Database::SelectChannelMaxPasswordLenght(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT maxPasswordLenght FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["maxPasswordLenght"]->GetValueInt();
}

void				Database::UpdateChannelMaxPasswordLenght(const unsigned int id, const unsigned int maxPasswordLenght)
{
	std::stringstream	query;
	query << "UPDATE channels SET maxPasswordLenght=" << maxPasswordLenght << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const std::string&	Database::SelectChannelMotd(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT motd FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["motd"]->GetValueString();
}

void				Database::UpdateChannelMotd(const unsigned int id, const std::string& motd)
{
	std::stringstream	query;
	query << "UPDATE channels SET motd=" << motd << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const unsigned int	Database::SelectChannelMaxMotdLenght(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT maxMotdLenght FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["maxMotdLenght"]->GetValueInt();
}

void				Database::UpdateChannelMaxMotdLenght(const unsigned int id, const unsigned int maxMotdLenght)
{
	std::stringstream	query;
	query << "UPDATE channels SET maxMotdLenght=" << maxMotdLenght << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const std::string&	Database::SelectChannelDescription(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT description FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["description"]->GetValueString();
}

void				Database::UpdateChannelDescription(const unsigned int id, const std::string& description)
{
	std::stringstream	query;
	query << "UPDATE channels SET description=" << description << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const unsigned int	Database::SelectChannelMaxDesciptionLenght(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT maxDescriptionLenght FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["maxDescriptionLenght"]->GetValueInt();
}

void				Database::UpdateChannelMaxDesciptionLenght(const unsigned int id, const unsigned int maxDescriptionLenght)
{
	std::stringstream	query;
	query << "UPDATE channels SET maxDescriptionLenght=" << maxDescriptionLenght << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const bool			Database::SelectChannelVisibility(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT visible FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["visible"]->GetValueBool();
}

void				Database::UpdateChannelVisibility(const unsigned int id, const bool visible)
{
	std::stringstream	query;
	query << "UPDATE channels SET visibility=" << visible << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const unsigned int	Database::SelectChannelMaxUser(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT maxUser FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["maxUser"]->GetValueInt();
}

void				Database::UpdateChannelMaxUser(const unsigned int id, const unsigned int maxUser)
{
	std::stringstream	query;
	query << "UPDATE channels SET maxUser=" << maxUser << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const unsigned int	Database::SelectChannelMaxTalker(const unsigned int id)
{
	DbResult	result;
	std::stringstream	query;
	query << "SELECT maxTalker FROM channels WHERE id=" << id << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["maxTalker"]->GetValueInt();
}

void				Database::UpdateChannelMaxTalker(const unsigned int id, const unsigned int maxTalker)
{
	std::stringstream	query;
	query << "UPDATE channels SET maxTalker=" << maxTalker << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

IDatabase::DbResult*		Database::SelectChannelRight(const unsigned int id)
{
	//load depuis la base le right de id
	DbResult*	result = new DbResult();
	std::stringstream	query;
	query << "SELECT * FROM channelrights WHERE id=" << id << ";";
	this->Execute(query.str(), result);
	return result;
}

IDatabase::DbResult*		Database::SelectChannelRight(const unsigned int idUser, const unsigned int idChannel)
{
	//load depuis la base le right de user/channel
	DbResult*	result = new DbResult();
	std::stringstream	query;
	query << "SELECT * FROM channelrights WHERE idUser=" << idUser << " AND idChannel=" << idChannel << ";";
	this->Execute(query.str(), result);
	return result;
}

const unsigned int		Database::SelectChannelRightId(const unsigned int idUser, const unsigned int idChannel)
{
	//load depuis la base le id d'un right
	DbResult	result;
	std::stringstream	query;
	query << "SELECT id FROM channelrights WHERE idUser=" << idUser << " AND idChannel=" << idChannel << ";";
	this->Execute(query.str(), &result);
	return (*result[0])["id"]->GetValueInt();
}

const unsigned int	Database::CreateChannelRightDefault(const unsigned int idUser, const unsigned int idChannel)
{
	DbResult*	result = this->SelectChannelRight(0);
	std::stringstream	query;
	query << "INSERT INTO channelrights(canCreate, canDelete, canEdit, canEditUser, canTalk, canWrite, canKick, canBan) VALUES(" << (*result->at(0))["canCreate"]->GetValueInt() << "," << (*result->at(0))["canDelete"]->GetValueInt() << "," << (*result->at(0))["canEdit"]->GetValueInt() << "," << (*result->at(0))["canEditUser"]->GetValueInt() << "," << (*result->at(0))["canTalk"]->GetValueInt() << "," << (*result->at(0))["canWrite"]->GetValueInt() << "," << (*result->at(0))["canKick"]->GetValueInt() << "," << (*result->at(0))["canBan"]->GetValueInt() << ");";
	this->Execute(query.str());
	delete result;
	return this->SelectChannelRightId(idUser, idChannel);
}

void				Database::UpdateChannelRight(const unsigned int id, const bool canCreate, const bool canDelete, const bool canEdit, const bool canEditUser, const bool canTalk, const bool canWrite, const bool canKick, const bool canBan)
{
	std::stringstream	query;
	query << "UPDATE channelrights SET canCreate=" << (int)canCreate << ", canDelete=" << (int)canDelete << ", canEdit=" << (int)canEdit << ", canEditUser=" << (int)canEditUser << ", canTalk=" << (int)canTalk << ", canWrite=" << (int)canWrite << ", canKick=" << (int)canKick << ", canBan=" << (int)canBan << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

void				Database::UpdateChannelRightCanCreateChild(const unsigned int id, const bool value)
{
	std::stringstream	query;
	query << "UPDATE channelrights SET canCreate=" << (int)value << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

void				Database::UpdateChannelRightCanDeleteChild(const unsigned int id, const bool value)
{
	std::stringstream	query;
	query << "UPDATE channelrights SET canDelete=" << (int)value << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

void				Database::UpdateChannelRightCanEdit(const unsigned int id, const bool value)
{
	std::stringstream	query;
	query << "UPDATE channelrights SET canEdit=" << (int)value << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

void				Database::UpdateChannelRightCanEditUserRight(const unsigned int id, const bool value)
{
	std::stringstream	query;
	query << "UPDATE channelrights SET canEditUser=" << (int)value << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

void				Database::UpdateChannelRightCanTalk(const unsigned int id, const bool value)
{
	std::stringstream	query;
	query << "UPDATE channelrights SET canTalk=" << (int)value << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

void				Database::UpdateChannelRightCanWrite(const unsigned int id, const bool value)
{
	std::stringstream	query;
	query << "UPDATE channelrights SET canWrite=" << (int)value << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

void				Database::UpdateChannelRightCanKick(const unsigned int id, const bool value)
{
	std::stringstream	query;
	query << "UPDATE channelrights SET canKick=" << (int)value << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

void				Database::UpdateChannelRightCanBan(const unsigned int id, const bool value)
{
	std::stringstream	query;
	query << "UPDATE channelrights SET canBan=" << (int)value << " WHERE id=" << id << ";";
	this->Execute(query.str());
}

const std::string&	Database::SelectServerName()
{
	//load depuis la base le name du server
	DbResult	result;
	std::stringstream	query;
	query << "SELECT name FROM server WHERE id=0;";
	this->Execute(query.str(), &result);
	return (*result[0])["name"]->GetValueString();
}

void				Database::UpdateServerName(const std::string& name)
{
	std::stringstream	query;
	query << "UPDATE server SET name=" << name << " WHERE id=0;";
	this->Execute(query.str());
}

IDatabase::DbResult*		Database::SelectServerBanListLogin()
{
	DbResult*	result = new DbResult();
	std::stringstream	query;
	query << "SELECT login FROM banListLogin;";
	this->Execute(query.str(), result);
	return result;
}

void				Database::InsertServerBanListLogin(const std::string& login)
{
	std::stringstream	query;
	query << "INSERT INTO banListLogin(login) VALUE(" << login << ");";
	this->Execute(query.str());
}

void				Database::DeleteServerBanListLogin(const std::string& login)
{
	std::stringstream	query;
	query << "DELETE FROM banListLogin WHERE login=" << login << ";";
	this->Execute(query.str());
}
